package org.jugile.util;

import java.io.File;
import java.util.ArrayList;
import java.util.List;

public class SExpr extends Jugile {
	public static void main(String args[]) {
		SExpr s = parse("data/test.lisp");
		//SExpr s = parse("data/tappio-test.tlk");
		print("s:\n" + s); 
	}
	
	public static SExpr parse(String fname) {
		Tokens tk = new Tokens(fname);
		String token = null;
		SExpr root = new SExpr();
		SExpr current = root;
		while ((token = tk.next()) != null) {
			//print("token: " + token);
			if ("(".equals(token)) {
				SExpr s = new SExpr();
				current.add(s);
				current = s;
				continue;
			}
			if (")".equals(token)) {
				current = current.parent;
				continue;
			}
			current.add(new SExpr(token));
		}
		if (root.children.size() > 0) root = root.children.get(0);
		return root;
	}
	
	private List<SExpr> children = new ArrayList<SExpr>();
	public List<SExpr> children() { return children; }
	private SExpr parent = null;
	public void add(SExpr e) { children.add(e); e.parent = this; }
	public List<SExpr> tail() {
		if (children.size() > 1) return children.subList(1,children.size());
		return new ArrayList<SExpr>();
	}
	public SExpr head() { 
		if (children.size() > 0) return children.get(0);
		return null;
	}
	
	public SExpr get(int idx) { return children.get(idx); }
	public String str(int idx) { return get(idx).getValue(); }
	
	private String value;
	public String getValue() { return value; }
	
	public SExpr() { }
	public SExpr(String v) { value = v; }
	
	public String toString() {
		Buffer buf = new Buffer(" ");
		dump(buf);
		return buf.toString().trim();
	}
	public void dump(Buffer buf) {
		if (!empty(value)) {
			buf.add(value + " ");
		} else {
			buf.nl();
			buf.ind();
			buf.add("(");
			buf.incrIndent();
			for (SExpr s : children) {
				s.dump(buf);
			}
			buf.add(")");
			buf.decrIndent();
		}
	}
	
	public static class Tokens {
		PullReader pr;
		public Tokens(String fname) {
			pr = new PullReader(new File(fname));
		}
		public String next() {
			pr.trim();
			Buffer buf = new Buffer();
			boolean string = false;
			while (pr.hasNext()) {
				char ch = pr.next();
				//print("  ch: " + ch);
				if (ch == '\\') {
					ch = pr.next();
					buf.add(ch);
					continue;
				}
				if (!string) {
					if (ch == '(') return ""+ch;
					if (ch == ')') return ""+ch;
					char peek = pr.peek();
					if (peek == ')' || Character.isWhitespace(peek)) {
						buf.add(ch);
						return buf.toString();
					}
				}
				if (ch == '"') {
					buf.add(ch);
					if (string) return buf.toString();
					string = true;
					continue;
				}
				buf.add(ch);
			}
			return null;
		}
	}
	
}
